package com.evil.application.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.evil.account.api.RemoteUserEnterpriseService;
import com.evil.application.api.RemoteApplicationFormService;
import com.evil.application.mapper.ApplicationFormMapper;
import com.evil.application.mapper.ApplicationInfoMapper;
import com.evil.application.pojo.dto.form.*;
import com.evil.application.pojo.dto.form.control.save.CopyControlDTO;
import com.evil.application.pojo.dto.form.enclosure.FormEnclosureDTO;
import com.evil.application.pojo.dto.form.rule.SnRuleRespDTO;
import com.evil.application.pojo.entity.ApplicationForm;
import com.evil.application.pojo.entity.ApplicationInfo;
import com.evil.application.service.ApplicationFormControlService;
import com.evil.application.service.ApplicationFormEnclosureService;
import com.evil.application.service.ApplicationFormService;
import com.evil.application.service.ApplicationFormSnRuleService;
import com.evil.common.account.util.LoginUtil;
import com.evil.common.application.dto.ApplicationIdVersionReqDTO;
import com.evil.common.application.dto.ApplicationIdsVersionReqDTO;
import com.evil.common.application.dto.ApplicationKeyVersionReqDTO;
import com.evil.common.application.dto.form.SpecialControlConfigDTO;
import com.evil.common.application.enums.ApplicationVersionEnum;
import com.evil.common.application.enums.PublishStatusEnum;
import com.evil.common.application.enums.form.SnRuleTypeEnum;
import com.evil.common.application.util.ApplicationRedisKeyUtil;
import com.evil.common.core.enums.BaseEnum;
import com.evil.common.core.exception.BusinessException;
import com.evil.common.core.util.StreamUtil;
import com.evil.common.family.dto.FindByIdsReqDTO;
import com.evil.common.family.enums.SpecialFieldEnum;
import com.evil.common.core.enums.SwitchEnum;
import com.evil.common.redis.util.CacheUtil;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RestController;

import java.util.*;

/**
 * 应用表单表
 *
 * @author liyang
 * @date 2022-08-08 00:00
 */
@Slf4j
@RestController
@AllArgsConstructor
public class ApplicationFormServiceImpl extends ServiceImpl<ApplicationFormMapper, ApplicationForm> implements ApplicationFormService, RemoteApplicationFormService {

    private final LoginUtil loginUtil;

    private final CacheUtil cacheUtil;

    private final ApplicationInfoMapper applicationInfoMapper;

    private final ApplicationFormEnclosureService applicationFormEnclosureService;

    private final ApplicationFormSnRuleService applicationFormSnRuleService;

    private final ApplicationFormControlService applicationFormControlService;

    private final RemoteUserEnterpriseService remoteUserEnterpriseService;

    private final ApplicationLogServiceImpl logService;

    /**
     * 应用表单
     *
     * @param id id
     */
    @Override
    public Optional<ApplicationForm> isExistById(Long id) {
        return this.baseMapper.isExistById(id);
    }

    /**
     * 应用表单
     *
     * @param id id
     */
    @Override
    public ApplicationForm findById(Long id) {
        return this.baseMapper.findById(id);
    }

    /**
     * 应用表单
     *
     * @param applicationIdVersionReqDTO applicationIdVersionReqDTO
     */
    @Override
    public ApplicationForm findByAppIdAndVersion(ApplicationIdVersionReqDTO applicationIdVersionReqDTO) {
        return this.baseMapper.findByAppIdAndVersion(applicationIdVersionReqDTO.getApplicationId(), applicationIdVersionReqDTO.getApplicationVersion());
    }

    /**
     * 应用表单列表
     *
     * @param findByIdsReqDTO findByIdsReqDTO
     */
    @Override
    public List<ApplicationForm> findByIds(FindByIdsReqDTO findByIdsReqDTO) {
        if (findByIdsReqDTO.getCache()) {
            return this.baseMapper.findByIds_Cache(findByIdsReqDTO.getIds(), findByIdsReqDTO.getFilterDeleted());
        } else {
            return this.baseMapper.findByIds(findByIdsReqDTO.getIds(), findByIdsReqDTO.getFilterDeleted());
        }
    }

    /**
     * 应用表单列表
     *
     * @param applicationIdsVersionReqDTO applicationIdsVersionReqDTO
     */
    @Override
    public List<ApplicationForm> findByAppIdsAndVersion(ApplicationIdsVersionReqDTO applicationIdsVersionReqDTO) {
        return this.baseMapper.findByAppIdsAndVersion(applicationIdsVersionReqDTO.getApplicationIds(), applicationIdsVersionReqDTO.getApplicationVersion());
    }

    /**
     * 应用表单列表
     *
     * @param findByIdsReqDTO findByIdsReqDTO
     */
    @Override
    public Map<Long, ApplicationForm> findMapByIds(FindByIdsReqDTO findByIdsReqDTO) {
        return StreamUtil.toOrderlyMapK(this.findByIds(findByIdsReqDTO), ApplicationForm::getFormId);
    }

    /**
     * 发布版表单
     *
     * @param appId appId
     * @return ApplicationForm
     */
    @Override
    public ApplicationForm releaseForm(Long appId) {
        return this.baseMapper.findByAppIdAndVersion(appId, ApplicationVersionEnum.RELEASE_VERSION.getId());
    }

    /**
     * 发布版表单
     *
     * @param app app
     * @return ApplicationForm
     */
    @Override
    public ApplicationForm releaseForm(ApplicationInfo app) {
        return this.releaseForm(app.getApplicationId());
    }

    /**
     * 应用表单信息
     *
     * @param applicationKeyVersionReqDTO applicationKeyVersionReqDTO
     */
    @Override
    public ApplicationFormRespDTO applicationFormInfo(ApplicationKeyVersionReqDTO applicationKeyVersionReqDTO) {
        String applicationKey = applicationKeyVersionReqDTO.getApplicationKey();
        Integer applicationVersion = applicationKeyVersionReqDTO.getApplicationVersion();

        // 获取版本
        ApplicationVersionEnum versionEnum = ApplicationVersionEnum.findById(applicationVersion);

        // 应用信息
        ApplicationInfo applicationInfo = applicationInfoMapper.findByKey(applicationKey);
        // 检查企业
        loginUtil.checkEnterpriseId(applicationInfo.getEnterpriseId());

        // 表单信息
        ApplicationForm applicationForm = this.baseMapper.findByAppIdAndVersion(applicationInfo.getApplicationId(), versionEnum.getId());

        // 表单编号生成规则
        List<SnRuleRespDTO> snRules = applicationFormSnRuleService.findBaseByFormId(applicationForm.getFormId());
        // 表单附件
        List<FormEnclosureDTO> formEnclosures = applicationFormEnclosureService.findBaseByFormId(applicationForm.getFormId());

        ApplicationFormRespDTO applicationFormRespDTO = new ApplicationFormRespDTO();
        applicationFormRespDTO.setApplicationKey(applicationKey);
        applicationFormRespDTO.setFormId(applicationForm.getFormId());
        applicationFormRespDTO.setFormName(applicationForm.getFormName());
        applicationFormRespDTO.setFormDescription(applicationForm.getFormDescription());
        applicationFormRespDTO.setSnRuleType(applicationForm.getSnRuleType());
        applicationFormRespDTO.setSnRules(snRules);
        applicationFormRespDTO.setFormEnclosures(formEnclosures);
        applicationFormRespDTO.setPublishStatus(applicationInfo.getPublishStatus());
        applicationFormRespDTO.setSpecialControlConfig(JSON.parseObject(applicationForm.getSpecialControlConfig(), SpecialControlConfigDTO.class));
        return applicationFormRespDTO;
    }

    /**
     * 应用表单信息
     *
     * @param applicationFormBaseReqDTO applicationFormBaseReqDTO
     */
    @Override
    public List<ApplicationFormBaseRespDTO> applicationFormBases(ApplicationFormBaseReqDTO applicationFormBaseReqDTO) {
        Long enterpriseId = loginUtil.getParamEnterpriseId(applicationFormBaseReqDTO.getEnterpriseId());
        List<ApplicationInfo> apps = applicationInfoMapper.findByEnterpriseId(enterpriseId);
        Map<Long, String> appKeyMap = StreamUtil.toMapKV(apps, ApplicationInfo::getApplicationId, ApplicationInfo::getApplicationKey);
        ApplicationIdsVersionReqDTO versionReqDTO = new ApplicationIdsVersionReqDTO(appKeyMap.keySet(), ApplicationVersionEnum.RELEASE_VERSION.getId());
        List<ApplicationForm> forms = this.findByAppIdsAndVersion(versionReqDTO);
        return StreamUtil.transListT(forms, e -> {
            ApplicationFormBaseRespDTO baseRespDTO = BeanUtil.copyProperties(e, ApplicationFormBaseRespDTO.class);
            baseRespDTO.setApplicationKey(appKeyMap.get(e.getApplicationId()));
            return baseRespDTO;
        });
    }

    /**
     * 编辑应用表单
     *
     * @param modifyApplicationFormReqDTO modifyApplicationFormReqDTO
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public synchronized void modifyApplicationForm(ModifyApplicationFormReqDTO modifyApplicationFormReqDTO) {
        // 检查是否是管理员
        remoteUserEnterpriseService.checkEnterpriseManager(loginUtil.getLoginUserEnterprise());

        String applicationKey = modifyApplicationFormReqDTO.getApplicationKey();
        Long applicationFormId = modifyApplicationFormReqDTO.getFormId();

        ApplicationForm applicationForm = this.baseMapper.findById(applicationFormId);
        // 不允许编辑发布版
        if (ApplicationVersionEnum.RELEASE_VERSION.getId() == applicationForm.getApplicationVersion()) {
            throw new BusinessException(BaseEnum.ACCESS_DENIED);
        }
        Integer version = applicationForm.getApplicationVersion();

        ApplicationInfo applicationInfo = applicationInfoMapper.findByKey(applicationKey);
        // 检查企业
        loginUtil.checkEnterpriseId(applicationInfo.getEnterpriseId());
        applicationForm.setFormName(modifyApplicationFormReqDTO.getFormName());
        applicationForm.setFormDescription(modifyApplicationFormReqDTO.getFormDescription());
        applicationForm.setSnRuleType(modifyApplicationFormReqDTO.getSnRuleType());
        applicationForm.setSpecialControlConfig(JSON.toJSONString(modifyApplicationFormReqDTO.getSpecialControlConfig()));

        cacheUtil.clearRequestCache(() -> {
            logService.saveAndCheck(this.baseMapper, ApplicationForm::getFormId, applicationForm);

            // 表单编号生成规则
            if (SnRuleTypeEnum.DIY.getId() == modifyApplicationFormReqDTO.getSnRuleType()) {
                applicationFormSnRuleService.modifyFormSnRule(applicationForm, modifyApplicationFormReqDTO.getSnRules());
            }

            // 表单附件
            applicationFormEnclosureService.modifyFormEnclosure(applicationForm, modifyApplicationFormReqDTO.getFormEnclosures());

            // 有未部署
            applicationInfo.setPublishStatus(PublishStatusEnum.UN_PUBLISH.getId());
            logService.saveAndCheck(applicationInfoMapper, ApplicationInfo::getApplicationId, applicationInfo);

            // 记录 清理应用表单缓存
            cacheUtil.addClearRequestCache(ApplicationRedisKeyUtil.applicationFormKey(applicationInfo.getApplicationId(), version));
            cacheUtil.addClearRequestCache(ApplicationRedisKeyUtil.formKey(applicationForm.getFormId()));
        });
    }

    /**
     * 初始化应用表单
     *
     * @param applicationInfo applicationInfo
     */
    @Override
    public void initForm(ApplicationInfo applicationInfo) {
        // 先初始化设计版
        ApplicationForm designForm = this.baseMapper.isExistByAppIdAndVersion(
                        applicationInfo.getApplicationId(), ApplicationVersionEnum.DESIGN_VERSION.getId())
                // 不存在得补
                .orElseGet(() -> this.createApplicationForm(applicationInfo, ApplicationVersionEnum.DESIGN_VERSION));

        // 再初始化发布版
        ApplicationForm releaseForm = this.baseMapper.isExistByAppIdAndVersion(
                        applicationInfo.getApplicationId(), ApplicationVersionEnum.RELEASE_VERSION.getId())
                // 不存在得补
                .orElseGet(() -> this.createApplicationForm(applicationInfo, ApplicationVersionEnum.RELEASE_VERSION));

        // 再直接发布表单控件
        applicationFormControlService.copyControls(applicationInfo, designForm, releaseForm, true);
    }

    /**
     * 发布应用表单
     *
     * @param applicationInfo applicationInfo
     * @return CopyControlRespDTO
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public CopyControlDTO publishApplicationForm(ApplicationInfo applicationInfo) {
        return this.replaceApplicationForm(applicationInfo, true);
    }

    /**
     * 还原应用表单
     *
     * @param applicationInfo applicationInfo
     * @return CopyControlRespDTO
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public CopyControlDTO restoreApplicationForm(ApplicationInfo applicationInfo) {
        return this.replaceApplicationForm(applicationInfo, false);
    }

    /**
     * 删除应用表单
     *
     * @param applicationInfo applicationInfo
     */
    @Override
    public void deleteForm(ApplicationInfo applicationInfo) {
        ApplicationVersionEnum.forEach(em -> {
            ApplicationForm applicationForm = this.baseMapper.findByAppIdAndVersion(applicationInfo.getApplicationId(), em.getId());
            applicationForm.setIsDeleted(SwitchEnum.YES.getId());

            logService.saveAndCheck(this.baseMapper, ApplicationForm::getFormId, applicationForm);

            // 删除表单生成规则
            applicationFormSnRuleService.deleteFormSnRule(applicationForm);

            // 删除表单附件
            applicationFormEnclosureService.deleteFormEnclosure(applicationForm);

            // 删除表单控件
            applicationFormControlService.deleteFormControl(applicationForm);

            // 移除企业应用树缓存
            cacheUtil.addClearRequestCache(ApplicationRedisKeyUtil.enterpriseAppTreeKey(applicationInfo.getEnterpriseId(), em.getId()));
            // 移除企业应用控件缓存
            cacheUtil.addClearRequestCache(ApplicationRedisKeyUtil.enterpriseAppControlsKey(applicationInfo.getEnterpriseId(), applicationInfo.getApplicationId(), em.getId()));
        });
    }

    /**
     * 复制应用表单
     *
     * @param copyAppFormReqDTO copyAppFormReqDTO
     * @return 不同版本新旧表单控件id映射
     */
    @Override
    public Map<Integer, Map<Long, Long>> copyApplicationForm(CopyAppFormReqDTO copyAppFormReqDTO) {
        // 版本控件id映射
        Map<Integer, Map<Long, Long>> copyMap = new LinkedHashMap<>();
        // 版本控件id映射整合
        Map<Long, Long> controlIdMap = new HashMap<>();
        // 版本源控件id映射整合
        Map<Long, Long> controlSourceIdMap = new HashMap<>();

        ApplicationVersionEnum.forEach(em -> {
            CopyAppFormRespDTO copyAppFormRespDTO = this.copyApplicationForm(copyAppFormReqDTO, em);
            CopyControlDTO copyControlDTO = copyAppFormRespDTO.getCopyControlDTO();

            copyMap.put(em.getId(), copyControlDTO.getControlIdMap());
            controlIdMap.putAll(copyControlDTO.getControlIdMap());
            controlSourceIdMap.putAll(copyControlDTO.getControlSourceIdMap());
        });

        // 更新 复制后 新控件 源控件id信息
        applicationFormControlService.modifyControlSourceId(controlIdMap, controlSourceIdMap);

        return copyMap;
    }

    /**
     * 替换表单信息
     *
     * @param applicationInfo applicationInfo
     * @param isPublish       isPublish
     * @return CopyControlRespDTO
     */
    private CopyControlDTO replaceApplicationForm(ApplicationInfo applicationInfo, boolean isPublish) {
        // 发布版
        int releaseVersion = ApplicationVersionEnum.RELEASE_VERSION.getId();
        // 设计版
        int designVersion = ApplicationVersionEnum.DESIGN_VERSION.getId();
        ApplicationForm releaseVersionForm = this.baseMapper.findByAppIdAndVersion(applicationInfo.getApplicationId(), releaseVersion);
        ApplicationForm designVersionForm = this.baseMapper.findByAppIdAndVersion(applicationInfo.getApplicationId(), designVersion);

        CopyControlDTO copyControlDTO;
        if (isPublish) {
            // 发布：表单名 -> 应用名称
            applicationInfo.setApplicationName(designVersionForm.getFormName());
            // 发布：设计版 -> 发布版
            copyControlDTO = this.replaceApplicationForm(applicationInfo, designVersionForm, releaseVersionForm);

            // TODO 发布，动态生成表
        } else {
            // 还原：发布版 -> 设计版
            copyControlDTO = this.replaceApplicationForm(applicationInfo, releaseVersionForm, designVersionForm);
        }

        // 记录 清理应用表单缓存
        cacheUtil.addClearRequestCache(ApplicationRedisKeyUtil.applicationFormKey(applicationInfo.getApplicationId(), isPublish ? releaseVersion : designVersion));
        return copyControlDTO;
    }

    /**
     * 替换表单信息
     *
     * @param source source
     * @param target target
     * @return CopyControlRespDTO
     */
    private CopyControlDTO replaceApplicationForm(ApplicationInfo applicationInfo, ApplicationForm source, ApplicationForm target) {
        target.setFormName(source.getFormName());
        target.setFormDescription(source.getFormDescription());
        target.setSnRuleType(source.getSnRuleType());
        target.setSpecialControlConfig(source.getSpecialControlConfig());

        logService.saveAndCheck(this.baseMapper, ApplicationForm::getFormId, target);

        // 先 删除表单附件
        applicationFormEnclosureService.deleteByFormId(target.getFormId());
        // 再 复制表单附件
        applicationFormEnclosureService.copyFormEnclosure(source, target);

        // 复制表单控件
        CopyControlDTO copyControlDTO = applicationFormControlService.copyControls(applicationInfo, source, target, true);

        // 先 删除表单生成规则
        applicationFormSnRuleService.deleteByFormId(target.getFormId());
        // 再 复制表单编号生成规则
        applicationFormSnRuleService.copyFormSnRule(source, target, copyControlDTO.getControlIdMap());

        return copyControlDTO;
    }

    /**
     * 复制表单
     *
     * @param copyAppFormReqDTO copyAppFormReqDTO
     * @param em                em
     * @return Map 新旧表单控件id映射
     */
    private CopyAppFormRespDTO copyApplicationForm(CopyAppFormReqDTO copyAppFormReqDTO, ApplicationVersionEnum em) {
        ApplicationInfo source = copyAppFormReqDTO.getSource();
        ApplicationInfo target = copyAppFormReqDTO.getTarget();
        ApplicationForm sourceForm = this.baseMapper.findByAppIdAndVersion(source.getApplicationId(), em.getId());

        ApplicationForm copyForm = BeanUtil.copyProperties(sourceForm, ApplicationForm.class, SpecialFieldEnum.getSpecialFields());
        copyForm.setFormId(null);
        copyForm.setApplicationId(target.getApplicationId());
        copyForm.setFormName(target.getApplicationName());

        logService.saveAndCheck(this.baseMapper, ApplicationForm::getFormId, copyForm);

        // 复制表单附件
        applicationFormEnclosureService.copyFormEnclosure(sourceForm, copyForm);

        // 复制表单控件
        CopyControlDTO copyControlDTO = applicationFormControlService.copyControls(target, sourceForm, copyForm, false);

        // 复制表单生成规则
        applicationFormSnRuleService.copyFormSnRule(sourceForm, copyForm, copyControlDTO.getControlIdMap());

        return new CopyAppFormRespDTO(sourceForm, copyControlDTO);
    }

    /**
     * 创建表单
     *
     * @param applicationInfo applicationInfo
     * @param versionEnum     versionEnum
     * @return ApplicationForm
     */
    private ApplicationForm createApplicationForm(ApplicationInfo applicationInfo, ApplicationVersionEnum versionEnum) {
        ApplicationForm applicationForm = new ApplicationForm();
        applicationForm.setEnterpriseId(applicationInfo.getEnterpriseId());
        applicationForm.setApplicationId(applicationInfo.getApplicationId());
        applicationForm.setFormName(applicationInfo.getApplicationName());
        applicationForm.setApplicationVersion(versionEnum.getId());
        applicationForm.setSnRuleType(SnRuleTypeEnum.DEFAULT.getId());
        applicationForm.setSpecialControlConfig(JSON.toJSONString(new SpecialControlConfigDTO()));
        applicationForm.setIsDeleted(SwitchEnum.NO.getId());

        logService.saveAndCheck(this.baseMapper, ApplicationForm::getFormId, applicationForm);

        // 创建表单规则
        applicationFormSnRuleService.createFormSnRule(applicationForm);

        return applicationForm;
    }
}
